package main

import (
	"encoding/json"
	"fmt"
	"strings"

	"github.com/hyperledger/fabric-contract-api-go/contractapi"
)

type CompanyRevenue struct {
	ObjectType string `json:"docType"`
	Name       string `json:"name"`  //公司名称
	PE         int    `json:"pe"`    //市盈率
	PB         int    `json:"pb"`    //市净率
	EPS        int    `json:"eps"`   //每股收益
	BVPS       int    `json:"bvps"`  //每股净资产
	BGI        string `json:"bgi"`   //营业总收入
	Owner      string `json:"owner"` //数据公司
}

type CompanyProfit struct {
	ObjectType string `json:"docType"`
	Name       string `json:"name"` //公司名称
	GPR        int    `json:"gpr"`  //毛利率
	NPR        int    `json:"npr"`  //净利率
}

type SmartContract struct {
	contractapi.Contract
}

func (s *SmartContract) InitCompany(ctx contractapi.TransactionContextInterface) error {
	transMap, err := ctx.GetStub().GetTransient()
	if err != nil {
		return fmt.Errorf("Error getting transient: " + err.Error())
	}
	transientCompanyJSON, ok := transMap["company"]
	if !ok {
		return fmt.Errorf("company not found in the transient map")
	}

	type companyTransientInput struct {
		Name  string `json:"name"`
		PE    int    `json:"pe"`    //市盈率
		PB    int    `json:"pb"`    //市净率
		EPS   int    `json:"eps"`   //每股收益
		BVPS  int    `json:"bvps"`  //每股净资产
		BGI   string `json:"bgi"`   //营业总收入
		Owner string `json:"owner"` //数据公司
		GPR   int    `json:"gpr"`   //毛利率
		NPR   int    `json:"npr"`   //净利率
	}

	var companyInput companyTransientInput
	err = json.Unmarshal(transientCompanyJSON, &companyInput)
	if err != nil {
		return fmt.Errorf("failed to unmarshal JSON: %s", err.Error())
	}
	if len(companyInput.Name) == 0 {
		return fmt.Errorf("name field must be a non-empty string")
	}
	if companyInput.PE <= 0 {
		return fmt.Errorf("pe field must be a positice integer")
	}
	if companyInput.PB <= 0 {
		return fmt.Errorf("pb field must be a positice integer")
	}
	if companyInput.EPS <= 0 {
		return fmt.Errorf("eps field must be a positice integer")
	}
	if companyInput.BVPS <= 0 {
		return fmt.Errorf("bvps field must be a positice integer")
	}
	if len(companyInput.BGI) == 0 {
		return fmt.Errorf("bgi field must be a non-empty string")
	}
	if companyInput.GPR <= 0 {
		return fmt.Errorf("gpr field must be a positice integer")
	}
	if companyInput.NPR <= 0 {
		return fmt.Errorf("npr field must be a positice integer")
	}

	companyAsBytes, err := ctx.GetStub().GetPrivateData("collectCompanyRevenue", companyInput.Name)
	if err != nil {
		return fmt.Errorf("Failed to get companyRevenue: " + err.Error())
	} else if companyAsBytes != nil {
		fmt.Println("This companyRevenue already exists: " + companyInput.Name)
		return fmt.Errorf("This companyRevenue already exists: " + companyInput.Name)
	}

	companyRevenue := &CompanyRevenue{
		ObjectType: "CompanyRevenue",
		Name:       companyInput.Name,
		PE:         companyInput.PE,
		PB:         companyInput.PB,
		EPS:        companyInput.EPS,
		BVPS:       companyInput.BVPS,
		BGI:        companyInput.BGI,
		Owner:      companyInput.Owner,
	}
	companyRevenueJSONasBytes, err := json.Marshal(companyRevenue)
	if err != nil {
		return fmt.Errorf(err.Error())
	}

	err = ctx.GetStub().PutPrivateData("collectionCompanyRevenue", companyInput.Name, companyRevenueJSONasBytes)
	if err != nil {
		return fmt.Errorf("failed to put CompanyRevenue: %s", err.Error())
	}

	companyProfit := CompanyProfit{
		ObjectType: "CompanyProfit",
		Name:       companyInput.Name,
		GPR:        companyInput.GPR,
		NPR:        companyInput.NPR,
	}
	companyProfitJSONasBytes, err := json.Marshal(companyProfit)
	if err != nil {
		return fmt.Errorf(err.Error())
	}

	err = ctx.GetStub().PutPrivateData("companyProfit", companyInput.Name, companyProfitJSONasBytes)
	if err != nil {
		return fmt.Errorf("failed to put CompanyProfit: %s", err.Error())
	}
	indexName := "bgi~name"
	bgiNameIndexKey, err := ctx.GetStub().CreateCompositeKey(indexName, []string{companyRevenue.BGI, companyRevenue.Name})
	if err != nil {
		return err
	}

	value := []byte{0x00}
	err = ctx.GetStub().PutPrivateData("collectionCompanyRevenue", bgiNameIndexKey, value)
	if err != nil {
		return err
	}

	return nil
}

func (s *SmartContract) ReadCompanyRevenue(ctx contractapi.TransactionContextInterface, companyRevenueID string) (*CompanyRevenue, error) {
	companyRevenueJSON, err := ctx.GetStub().GetPrivateData("collectionCompanyRevenue", companyRevenueID)
	if err != nil {
		return nil, fmt.Errorf("failed to read from marble %s", err.Error())
	}
	if companyRevenueJSON == nil {
		return nil, fmt.Errorf("%s does not exist", companyRevenueID)
	}
	companyRevenue := new(CompanyRevenue)
	_ = json.Unmarshal(companyRevenueJSON, companyRevenue)

	return companyRevenue, nil
}

func (s *SmartContract) ReadCompanyProfit(ctx contractapi.TransactionContextInterface, companyProfitID string) (*CompanyProfit, error) {
	companyProfitJSON, err := ctx.GetStub().GetPrivateData("collectionCompanyProfit", companyProfitID)
	if err != nil {
		return nil, fmt.Errorf("failed to read from company profit  %s", err.Error())
	}
	if companyProfitJSON == nil {
		return nil, fmt.Errorf("%s does not exist", companyProfitID)
	}

	companyProfit := new(CompanyProfit)
	_ = json.Unmarshal(companyProfitJSON, companyProfit)

	return companyProfit, nil
}

func (s *SmartContract) Delete(ctx contractapi.TransactionContextInterface) error {
	transMap, err := ctx.GetStub().GetTransient()
	if err != nil {
		return fmt.Errorf("Error getting transient: " + err.Error())
	}

	transientDeleteCompanyRevenueJSON, ok := transMap["companyRevenue_delete"]
	if !ok {
		return fmt.Errorf("companyRevenue to delete not found in the transient map")
	}

	type companyRevenueDelete struct {
		Name string `json:"name"`
	}

	var companyRevenueDeleteInput companyRevenueDelete
	err = json.Unmarshal(transientDeleteCompanyRevenueJSON, &companyRevenueDeleteInput)
	if err != nil {
		return fmt.Errorf("failed to unmarshal JSON: %s", err.Error())
	}
	if len(companyRevenueDeleteInput.Name) == 0 {
		return fmt.Errorf("name field must be a non-empty string")
	}

	valAsbytes, err := ctx.GetStub().GetPrivateData("collectionCompanyRevenue", companyRevenueDeleteInput.Name)
	if err != nil {
		return fmt.Errorf("failed to read companyRevenue: %s", err.Error())
	}
	if valAsbytes == nil {
		return fmt.Errorf("companyRevenue private details does not exist: %s", companyRevenueDeleteInput.Name)
	}

	var companyRevenueToDelete CompanyRevenue
	err = json.Unmarshal([]byte(valAsbytes), &companyRevenueToDelete)
	if err != nil {
		return fmt.Errorf("failed to unmarshal JSON: %s", err.Error())
	}

	err = ctx.GetStub().DelPrivateData("collectionCompanyRevenue", companyRevenueDeleteInput.Name)
	if err != nil {
		return fmt.Errorf("Failed to delete state:" + err.Error())
	}

	indexName := "bgi~name"
	bgiNameIndexKey, err := ctx.GetStub().CreateCompositeKey(indexName, []string{companyRevenueToDelete.BGI, companyRevenueToDelete.Name})
	if err != nil {
		return err
	}
	err = ctx.GetStub().DelPrivateData("collectionCompanyRevenue", bgiNameIndexKey)
	if err != nil {
		return fmt.Errorf("Failed to delete companyRevenue:" + err.Error())
	}

	err = ctx.GetStub().DelPrivateData("collectionCompanyProfit", companyRevenueDeleteInput.Name)
	if err != nil {
		return err
	}
	return nil
}

func (s *SmartContract) TransferCompanyRevenue(ctx contractapi.TransactionContextInterface) error {
	transMap, err := ctx.GetStub().GetTransient()
	if err != nil {
		return fmt.Errorf("Error getting transient: " + err.Error())
	}

	transientTransferCompanyRevenueJSON, ok := transMap["companyRevenue_owner"]
	if !ok {
		return fmt.Errorf("companyRevenue owner not found in the transient map")
	}

	type companyRevenueTransferTransientInput struct {
		Name  string `json:"name"`
		Owner string `json:"owner"`
	}

	var companyRevenueTransferInput companyRevenueTransferTransientInput
	err = json.Unmarshal(transientTransferCompanyRevenueJSON, &companyRevenueTransferInput)
	if err != nil {
		return fmt.Errorf("failed to unmarshal JSON: %s", err.Error())
	}

	if len(companyRevenueTransferInput.Name) == 0 {
		return fmt.Errorf("name field must be a non-empty string")
	}
	if len(companyRevenueTransferInput.Owner) == 0 {
		return fmt.Errorf("owner field must be a non-empty string")
	}

	companyRevenueAsBytes, err := ctx.GetStub().GetPrivateData("collectionCompanyRevenue", companyRevenueTransferInput.Name)
	if err != nil {
		return fmt.Errorf("Failed to get companyRevenue:" + err.Error())
	} else if companyRevenueAsBytes == nil {
		return fmt.Errorf("CompanyRevenue does not exist: " + companyRevenueTransferInput.Name)
	}

	companyRevenuetoTransfer := CompanyRevenue{}
	err = json.Unmarshal(companyRevenueAsBytes, &companyRevenuetoTransfer)
	if err != nil {
		return fmt.Errorf("failed to unmarshal JSON: %s", err.Error())
	}

	companyRevenuetoTransfer.Owner = companyRevenueTransferInput.Owner

	companyRevenueJSONasBytes, _ := json.Marshal(companyRevenuetoTransfer)
	err = ctx.GetStub().PutPrivateData("collectionCompanyRevenue", companyRevenuetoTransfer.Name, companyRevenueJSONasBytes)
	if err != nil {
		return err
	}

	return nil
}

func (s *SmartContract) GetCompanyRevenueByRange(ctx contractapi.TransactionContextInterface, startKey string, endKey string) ([]CompanyRevenue, error) {
	resultsIterator, err := ctx.GetStub().GetPrivateDataByRange("collectionCompanyRevenue", startKey, endKey)
	if err != nil {
		return nil, err
	}
	defer resultsIterator.Close()

	results := []CompanyRevenue{}
	for resultsIterator.HasNext() {
		response, err := resultsIterator.Next()
		if err != nil {
			return nil, err
		}

		newCompanyRevenue := new(CompanyRevenue)

		err = json.Unmarshal(response.Value, newCompanyRevenue)
		if err != nil {
			return nil, err
		}
		results = append(results, *newCompanyRevenue)
	}
	return results, nil
}

func (s *SmartContract) QueryCompanyRevenueByOwner(ctx contractapi.TransactionContextInterface, owner string) ([]CompanyRevenue, error) {
	ownerString := strings.ToLower(owner)

	queryString := fmt.Sprintf("{\"selector\":{\"docType\":\"companyRevenue\",\"owner\":\"%s\"}}", ownerString)

	queryResults, err := s.getQueryResultForQueryString(ctx, queryString)
	if err != nil {
		return nil, err
	}
	return queryResults, nil
}

func (s *SmartContract) QueryMCompanyRevenue(ctx contractapi.TransactionContextInterface, queryString string) ([]CompanyRevenue, error) {
	queryResults, err := s.getQueryResultForQueryString(ctx, queryString)
	if err != nil {
		return nil, err
	}
	return queryResults, nil
}
func (s *SmartContract) getQueryResultForQueryString(ctx contractapi.TransactionContextInterface, queryString string) ([]CompanyRevenue, error) {
	resultsIterator, err := ctx.GetStub().GetPrivateDataQueryResult("collectionCompanyRevenue", queryString)
	if err != nil {
		return nil, err
	}
	defer resultsIterator.Close()

	results := []CompanyRevenue{}

	for resultsIterator.HasNext() {
		response, err := resultsIterator.Next()
		if err != nil {
			return nil, err
		}

		newCompanyRevenue := new(CompanyRevenue)

		err = json.Unmarshal(response.Value, newCompanyRevenue)
		if err != nil {
			return nil, err
		}

		results = append(results, *newCompanyRevenue)
	}
	return results, nil
}

func (s *SmartContract) GetCompanyRevenueHash(ctx contractapi.TransactionContextInterface, collection string, companyRevenueID string) (string, error) {
	hashAsbytes, err := ctx.GetStub().GetPrivateDataHash(collection, companyRevenueID)
	if err != nil {
		return "", fmt.Errorf("Failed to get public data hash for companyRevenue:" + err.Error())
	} else if hashAsbytes == nil {
		return "", fmt.Errorf("CompanyRevenue does not exist: " + companyRevenueID)
	}
	return string(hashAsbytes), nil
}

func main() {
	chaincode, err := contractapi.NewChaincode(new(SmartContract))

	if err != nil {
		fmt.Printf("Error creating private companyRevenue chaincode: %s", err.Error())
		return
	}

	if err := chaincode.Start(); err != nil {
		fmt.Printf("Error starting private companyRevenue chaincode: %s", err.Error())
	}
}
